package puzzle.common.solver.topology;

import puzzle.common.point.Direction;

public class ClosureAnalyzer {
	public enum Type {
		FOUR_DIR, EIGHT_DIR
	}

	private int[] m_closureNumbers;
	private int m_width;
	private int m_height;

	public ClosureAnalyzer(ClosureAnalyzable g) {
		this(g, Type.FOUR_DIR);
	}

	public ClosureAnalyzer(ClosureAnalyzable g, Type type) {
		m_width = g.getWidth();
		m_height = g.getHeight();
		m_closureNumbers = new int[m_width * m_height];

		switch (type) {
		case FOUR_DIR:
			analyzeFourDirection(g);
			break;
		case EIGHT_DIR:
			analyzeEightDirection(g);
			break;
		}
	}

	private void analyzeFourDirection(ClosureAnalyzable g) {
		int closureNumber = 0;
		for (int y = 0; y < m_height; y++) {
			for (int x = 0; x < m_width; x++) {
				if (g.isClosureMember(x, y)) {
					boolean isNorthConnected = (y > 0) && g.isConnected(x, y, Direction.N);
					boolean isWestConnected = (x > 0) && g.isConnected(x, y, Direction.W);
					int north = isNorthConnected ? m_closureNumbers[x + (y - 1) * m_width] : 0;
					int west = isWestConnected ? m_closureNumbers[(x - 1) + y * m_width] : 0;

					int n;
					if (isNorthConnected) {
						n = north;
						if (isWestConnected && north != west) {
							replace(west, north, x, y);
						}
					} else {
						if (isWestConnected) {
							n = west;
						} else {
							n = ++closureNumber;
						}
					}
					m_closureNumbers[x + y * m_width] = n;
				}
			}
		}
	}
	
	
	private static final Direction[] DIRECTIONS = new Direction[] { Direction.NW, Direction.N,
			Direction.NE, Direction.W };

	private void analyzeEightDirection(ClosureAnalyzable g) {
		int closureNumber = 0;
		
		for (int y = 0; y < m_height; y++) {
			for (int x = 0; x < m_width; x++) {
				if (g.isClosureMember(x, y)) {
					int n = 0;
					for (Direction d : DIRECTIONS) {
						int x1 = x + d.v.dx;
						int y1 = y + d.v.dy;
						if (x1 >= 0 && x1 < m_width && y1 >= 0 && g.isConnected(x, y, d)) {
							int i = x1 + y1 * m_width;
							if (n == 0) {
								n = m_closureNumbers[i];
							} else {
								replace(m_closureNumbers[i], n, x, y);
							}
						}
					}
					m_closureNumbers[x + y * m_width] = (n != 0 ? n : ++closureNumber);
				}
			}
		}
	}

	private void replace(int from, int to, int x, int y) {
		for (int i = 0; i < x + y * m_width; i++) {
			if (m_closureNumbers[i] == from) {
				m_closureNumbers[i] = to;
			}
		}
	}

	public int getClosureNumber(int x, int y) {
		return m_closureNumbers[x + y * m_width];
	}
	
	public int[] getClosureNumbers() {
		return m_closureNumbers;
	}
}
